﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace BizElements.Core
{
    /// <summary>Represents a read-only collection of textual key/value pairs.</summary>
    public interface IPropertyCollection<T> : IEnumerable, ICollection, IEnumerable<KeyValuePair<string, T>>
    {
        /// <summary>Gets or sets, if the collection is not readonly, the element with the specified key.</summary>
        /// <value>The element with the specified key or <b>null</b> if the specified key does not exist.</value>
        T this[string key] { get; set; }

        /// <summary>Gets a value indicating whether the collection is read-only.</summary>
        /// <value>true if the collection is read-only; otherwise, false.</value>
        bool IsReadOnly { get; }

        /// <summary>Makes the collection read-only.</summary>
        void MakeReadOnly();
    }

    /// <summary>Represents a read-only collection of textual key/value pairs.</summary>
    [Serializable]
    public sealed class PropertyCollection<T> : IPropertyCollection<T>
    {
        Dictionary<string, T> properties;

        #region Constructors.

        /// <summary>Initializes a new instance of <b>PropertyCollection</b> class.</summary>
        public PropertyCollection()
        {
            this.properties = new Dictionary<string, T>();
        }

        /// <summary>Initializes a new instance of <b>PropertyCollection</b> class.</summary>
        /// <param name="readOnly">Indicated whether the collection is read-only.</param>
        public PropertyCollection(bool readOnly)
        {
            this.IsReadOnly = readOnly;
            this.properties = new Dictionary<string, T>();
        }

        /// <summary>Initializes a new instance of <b>PropertyCollection</b> class.</summary>
        /// <param name="readOnly">Indicated whether the collection is read-only.</param>
        /// <param name="properties">A dictionary which contains property names and values.</param>
        public PropertyCollection(bool readOnly, Dictionary<string, T> properties)
        {
            this.IsReadOnly = readOnly;
            this.properties = properties;            
        }

        #endregion

        #region IPropertyCollection.

        /// <summary>Gets or the element with the specified key.</summary>
        /// <value>The element with the specified key or <b>null</b> if the specified key does not exist.</value>
        public T this[string key]
        {
            get
            {
                T value;
                this.properties.TryGetValue(key, out value);
                return value;
            }
            set
            {
                if (this.IsReadOnly)
                    throw new InvalidOperationException(Messages.PropertyCollection_CollectionIsReadOnly);
                
                this.properties[key] = value;
            }
        }

        /// <summary>Gets a value indicating whether the collection is read-only. </summary>
        /// <value>true if the collection is read-only; otherwise, false.</value>
        public bool IsReadOnly { get; private set; }

        /// <summary>Makes the collection read-only.</summary>
        public void MakeReadOnly()
        {
            if (!this.IsReadOnly)
                this.IsReadOnly = true;
        }

        #endregion

        #region IEnumerable.

        /// <summary>Returns an enumerator that iterates through a collection.</summary>
        /// <value>An IEnumerator object that can be used to iterate through the collection.</value>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.properties.GetEnumerator();
        }

        #endregion

        #region ICollection.

        /// <summary>Copies the elements of the collection to an Array, starting at a particular Array index.</summary>
        /// <param name="array">The one-dimensional Array that is the destination of the elements copied from ICollection. The Array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        public void CopyTo(Array array, int index)
        {
            ((ICollection)this.properties).CopyTo(array, index);
        }

        /// <summary>Copies the elements of the collection to an Array, starting at a particular Array index.</summary>
        /// <param name="array">The one-dimensional Array that is the destination of the elements copied from ICollection. The Array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        public void CopyTo(KeyValuePair<string, string>[] array, int index)
        {
            ((ICollection)this.properties).CopyTo(array, index);
        }

        /// <summary>Gets the number of elements contained in the collection.</summary>
        /// <value>The number of elements contained in the collection.</value>
        public int Count
        {
            get { return this.properties.Count; }
        }

        /// <summary>Gets a value indicating whether access to the collection is synchronized (thread safe).</summary>
        /// <value><b>true</b> if access to the collection is synchronized (thread safe); otherwise, <b>false</b>.</value>
        bool ICollection.IsSynchronized
        {
            get { return ((ICollection)this.properties).IsSynchronized; }
        }

        /// <summary>Gets an object that can be used to synchronize access to the collection.</summary>
        /// <value>An object that can be used to synchronize access to the collection.</value>
        object ICollection.SyncRoot
        {
            get { return ((ICollection)this.properties).SyncRoot; }
        }

        #endregion

        #region IEnumerable<KeyValuePair<string, T>>.

        /// <summary>Returns an enumerator that iterates through the collection.</summary>
        /// <value>An enumerator that can be used to iterate through the collection.</value>
        public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
        {
            return this.properties.GetEnumerator();
        }

        #endregion
    }
}
